package com.laolei.core.datasource.config;

import java.sql.Statement;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import com.laolei.core.datasource.Packages;
import com.laolei.core.datasource.core.AbstractDataBaseBean;
import com.laolei.core.datasource.core.Mapper;
import com.laolei.core.datasource.core.Order;
import com.laolei.core.utils.StringUtils;

@Component
@ComponentScan(basePackageClasses = Packages.class)
public class DataBaseConfig extends AbstractDataBaseBean
		implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {

	static Logger log = LoggerFactory.getLogger(DataBaseConfig.class);

	ConfigurableEnvironment environment;

	@Override
	public void setEnvironment(Environment environment) {
		this.environment = (ConfigurableEnvironment) environment;
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		DruidConfig druidConfig = this.getDruidConfig();
		Configuration configuration = druidConfig.getConfiguration();
		
		DruidProperties defaultConfig = druidConfig.getDefaultConfig();
		Map<String, DruidNode> druidNodeConfigs = druidConfig.getDbNode();
		if (druidNodeConfigs == null || druidNodeConfigs.isEmpty())
			throw new RuntimeException("至少需要配置一个DataBase(配置DataBase参数在" + DruidConfig.dbprefix + ".db-node)");
		this.setPrimary(druidNodeConfigs).forEach((druidNodeName, druidNodeConfig) -> {
			try {
				Configuration _configuration;
				if(configuration==null)
					_configuration=new Configuration();
				else
					_configuration=configuration;
				this.registryBean(druidNodeName, druidNodeConfig, defaultConfig, _configuration, registry);
			} catch (Throwable e) {
				throw new RuntimeException(e);
			}
		});
	}

	private Map<String, DruidNode> setPrimary(Map<String, DruidNode> druidNodeConfigs) {
		int primarys = 0;
		DruidNode defDruidNode = null;
		for (Entry<String, DruidNode> entry : druidNodeConfigs.entrySet()) {
			DruidNode druidNode = entry.getValue();
			if (druidNode != null && druidNode.isPrimary()) {
				primarys++;
				if (primarys > 1)
					druidNode.setPrimary(false);
			}
			if (druidNode != null && defDruidNode == null)
				defDruidNode = druidNode;
		}
		if (primarys == 0 && defDruidNode != null)
			defDruidNode.setPrimary(true);
		return druidNodeConfigs;
	}

	private DruidConfig getDruidConfig() {
		PropertiesConfigurationFactory<DruidConfig> factory = new PropertiesConfigurationFactory<DruidConfig>(
				DruidConfig.class);
		factory.setPropertySources(environment.getPropertySources());
		factory.setConversionService(environment.getConversionService());
		factory.setIgnoreInvalidFields(false);
		factory.setIgnoreUnknownFields(true);
		factory.setExceptionIfInvalid(true);
		factory.setIgnoreNestedProperties(false);
		factory.setTargetName(DruidConfig.dbprefix);
		try {
			factory.bindPropertiesToTarget();
			return factory.getObject();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private void registryBean(String druidNodeName, DruidNode druidNodeConfig, DruidProperties defaultConfig,
			Configuration configuration, BeanDefinitionRegistry registry) {
		configuration.addKeyGenerator("not--01", new KeyGenerator() {
			
			@Override
			public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
				System.err.println("-----Before------>>>");
			}
			
			@Override
			public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
				System.err.println("------After----->>>");
			}
		});
		if (druidNodeConfig == null)
			return;
		String mapperPackage = druidNodeConfig.getMapperPackage();
		Order order = druidNodeConfig.getOrder();
		Mapper mappers = druidNodeConfig.getMapper();
		String basepackage = druidNodeConfig.getBasePackage();
		if (StringUtils.isEmpty(basepackage)) {
			log.warn("BasePackage为空，db配置失效,当前配置数据对象的名字{}", druidNodeName);
			return;
		}
		boolean primary = druidNodeConfig.isPrimary();

		String dataSourceName = druidNodeName + "-Laolei-DataSource";
		String jdbcTemplateName = druidNodeName + "-Laolei-JdbcTemplate";
		String transactionManagerName = druidNodeName + "-Laolei-TransactionManager";
		String sqlSessionFactoryBeanName = druidNodeName + "-Laolei-SqlSessionFactoryBean";
		String scannerConfigurerName = druidNodeName + "-Laolei-ScannerConfigurer";

		AbstractBeanDefinition dataSource = createDataSource(druidNodeConfig, defaultConfig, dataSourceName);
		AbstractBeanDefinition jdbcTemplate = createJdbcTemplate(dataSourceName);
		AbstractBeanDefinition transactionManager = createTransactionManager(dataSourceName);

		AbstractBeanDefinition sqlSessionFactoryBean = createSqlSessionFactoryBean(dataSourceName, mapperPackage,
				configuration);
		AbstractBeanDefinition scannerConfigurer = createScannerConfigurerBean(sqlSessionFactoryBeanName, basepackage,
				mappers, order);

		dataSource.setPrimary(primary);
		dataSource.setScope(BeanDefinition.SCOPE_SINGLETON);
		jdbcTemplate.setPrimary(primary);
		jdbcTemplate.setScope(BeanDefinition.SCOPE_SINGLETON);
		transactionManager.setPrimary(primary);
		transactionManager.setScope(BeanDefinition.SCOPE_SINGLETON);
		sqlSessionFactoryBean.setPrimary(primary);
		sqlSessionFactoryBean.setScope(BeanDefinition.SCOPE_SINGLETON);
		scannerConfigurer.setPrimary(primary);
		scannerConfigurer.setScope(BeanDefinition.SCOPE_SINGLETON);

		registry.registerBeanDefinition(dataSourceName, dataSource);
		registry.registerBeanDefinition(jdbcTemplateName, jdbcTemplate);
		registry.registerBeanDefinition(transactionManagerName, transactionManager);
		registry.registerBeanDefinition(sqlSessionFactoryBeanName, sqlSessionFactoryBean);
		registry.registerBeanDefinition(scannerConfigurerName, scannerConfigurer);
	}
}
